﻿using System;
using FireStarterModeller.Test;
using Habanero.Base.Exceptions;
using NUnit.Framework;
using Rhino.Mocks;

namespace Firestarter.DB.Schema.Test.Logic
{
    [TestFixture]
    public class TestSqlScriptRunner
    {
        // ReSharper disable InconsistentNaming

        [Test]
        public void Test_Construct_WhenDBUtilsParameterIsNull_ShouldThrowException()
        {
            //---------------Set up test pack-------------------
			
            //---------------Assert Precondition----------------

            try
            {
                //---------------Execute Test ----------------------
                new SqlScriptRunner(null);
                Assert.Fail("Expected to throw an Exception");
            } 
            catch (Exception ex)
            {
                //---------------Test Result -----------------------
                Assert.IsInstanceOf<HabaneroDeveloperException>(ex);
                StringAssert.Contains("utilsDB is null", ex.Message);
            }
        }

        [Test]
        public void Test_Construct_WhenDBUtilsParameterIsNotNullAndSqlScriptReaderIsNull_ShouldThrowException()
        {
            //---------------Set up test pack-------------------

            //---------------Assert Precondition----------------

            try
            {
                //---------------Execute Test ----------------------
                new SqlScriptRunner(new TestUtilsDB_SqlServer(), null);
                Assert.Fail("Expected to throw an Exception");
            }
            catch (Exception ex)
            {
                //---------------Test Result -----------------------
                Assert.IsInstanceOf<HabaneroDeveloperException>(ex);
                StringAssert.Contains("sqlScriptReader is null", ex.Message);
            }
        }

        [Test]
        public void Test_Construct_WhenDBUtilsParameterIsNullAndSqlScriptReaderIsNull_ShouldThrowException()
        {
            //---------------Set up test pack-------------------

            //---------------Assert Precondition----------------

            try
            {
                //---------------Execute Test ----------------------
                new SqlScriptRunner(null,null);
                Assert.Fail("Expected to throw an Exception");
            }
            catch (Exception ex)
            {
                //---------------Test Result -----------------------
                Assert.IsInstanceOf<HabaneroDeveloperException>(ex);
                StringAssert.Contains("utilsDB is null", ex.Message);
            }
        }

        [Test]
        public void Test_Construct_WhenDBUtilsParameterNotNull_ShouldReturnInstance()
        {
            //---------------Set up test pack-------------------
            var testUtilsDBSqlServer = new TestUtilsDB_SqlServer();
			
            //---------------Assert Precondition----------------
            Assert.IsNotNull(testUtilsDBSqlServer);
            //---------------Execute Test ----------------------
            var sqlScriptRunner = new SqlScriptRunner(testUtilsDBSqlServer);
            //---------------Test Result -----------------------
            Assert.IsNotNull(sqlScriptRunner);
            Assert.IsInstanceOf<SqlScriptRunner>(sqlScriptRunner);
        }

        [Test]
        public void Test_Construct_WhenDBUtilsParameterNotNullAndSqlScriptReaderIsNotNull_ShouldReturnInstance()
        {
            //---------------Set up test pack-------------------
            var testUtilsDBSqlServer = new TestUtilsDB_SqlServer();
            var sqlScriptReader = new SqlScriptReader();

            //---------------Assert Precondition----------------
            Assert.IsNotNull(testUtilsDBSqlServer);
            Assert.IsNotNull(sqlScriptReader);
            //---------------Execute Test ----------------------
            var sqlScriptRunner = new SqlScriptRunner(testUtilsDBSqlServer, sqlScriptReader);
            //---------------Test Result -----------------------
            Assert.IsNotNull(sqlScriptRunner);
            Assert.IsInstanceOf<SqlScriptRunner>(sqlScriptRunner);
        }

        [Test]
        public void Test_ExecuteScript_WhenCalledWithSqlScriptReaderHavingOneSqlStatement_ShouldCallLoadScriptAndExecuteOneSqlStatement()
        {
            //---------------Set up test pack-------------------

            var sqlStatement = GetRandomString();
            var utilsDbStub = MockRepository.GenerateStub<TestUtilsDB_SqlServer>();
            var scriptReaderMock = new SqlScriptReaderMock();
            scriptReaderMock.AddTestSqlStatment(sqlStatement);
            var sqlScriptRunner = new SqlScriptRunner(utilsDbStub, scriptReaderMock);
            //---------------Assert Precondition----------------
            Assert.AreEqual(1, scriptReaderMock.SqlStatements.Count);
            //---------------Execute Test ----------------------
            sqlScriptRunner.ExecuteScript("Script name not needed in this test");
            //---------------Test Result -----------------------
            Assert.IsTrue(scriptReaderMock.LoadScriptWasCalled);
            utilsDbStub.AssertWasCalled(x => x.ExecuteSql(utilsDbStub.CreateTestDBConnection(), sqlStatement));
        }

        [Test]
        public void Test_ExecuteScript_WhenCalledWithSqlScriptReaderHavingTwoSqlStatements_ShouldCallLoadScriptAndExecuteTwoSqlStatements()
        {
            //---------------Set up test pack-------------------

            var sqlStatement = GetRandomString();
            var sqlStatement2 = GetRandomString();
            var utilsDbStub = MockRepository.GenerateStub<TestUtilsDB_SqlServer>();
            var scriptReaderMock = new SqlScriptReaderMock();
            scriptReaderMock.AddTestSqlStatment(sqlStatement);
            scriptReaderMock.AddTestSqlStatment(sqlStatement2);
            var sqlScriptRunner = new SqlScriptRunner(utilsDbStub, scriptReaderMock);
            //---------------Assert Precondition----------------
            Assert.AreEqual(2, scriptReaderMock.SqlStatements.Count);
            //---------------Execute Test ----------------------
            sqlScriptRunner.ExecuteScript("Script name not needed in this test");
            //---------------Test Result -----------------------
            Assert.IsTrue(scriptReaderMock.LoadScriptWasCalled);
            utilsDbStub.AssertWasCalled(x => x.ExecuteSql(utilsDbStub.CreateTestDBConnection(), sqlStatement));
            utilsDbStub.AssertWasCalled(x => x.ExecuteSql(utilsDbStub.CreateTestDBConnection(), sqlStatement2));
        }

        [Test]
        public void Test_ExecuteScript_WhenCalledWithSqlScriptReaderHavingManySqlStatements_ShouldCallLoadScriptAndExecuteManySqlStatements()
        {
            //---------------Set up test pack-------------------

            var sqlStatement = GetRandomString();
            var sqlStatement2 = GetRandomString();
            var sqlStatement3 = GetRandomString();
            var utilsDbStub = MockRepository.GenerateStub<TestUtilsDB_SqlServer>();
            var scriptReaderMock = new SqlScriptReaderMock();
            scriptReaderMock.AddTestSqlStatment(sqlStatement);
            scriptReaderMock.AddTestSqlStatment(sqlStatement2);
            scriptReaderMock.AddTestSqlStatment(sqlStatement3);
            var sqlScriptRunner = new SqlScriptRunner(utilsDbStub, scriptReaderMock);
            //---------------Assert Precondition----------------
            Assert.AreEqual(3, scriptReaderMock.SqlStatements.Count);
            //---------------Execute Test ----------------------
            sqlScriptRunner.ExecuteScript("Script name not needed in this test");
            //---------------Test Result -----------------------
            Assert.IsTrue(scriptReaderMock.LoadScriptWasCalled);
            utilsDbStub.AssertWasCalled(x => x.ExecuteSql(utilsDbStub.CreateTestDBConnection(), sqlStatement));
            utilsDbStub.AssertWasCalled(x => x.ExecuteSql(utilsDbStub.CreateTestDBConnection(), sqlStatement2));
            utilsDbStub.AssertWasCalled(x => x.ExecuteSql(utilsDbStub.CreateTestDBConnection(), sqlStatement3));
        }

        [Test]
        public void Test_ExecuteScriptAgainstMasterDB_WhenCalledWithSqlScriptReaderHavingOneSqlStatement_ShouldCallLoadScriptAndExecuteOneSqlStatementAndChangesTestDbNameToMaster()
        {
            //---------------Set up test pack-------------------

            var sqlStatement = GetRandomString();
            var utilsDbStub = MockRepository.GenerateStub<TestUtilsDB_SqlServer>();
            var scriptReaderMock = new SqlScriptReaderMock();
            scriptReaderMock.AddTestSqlStatment(sqlStatement);
            var sqlScriptRunner = new SqlScriptRunner(utilsDbStub, scriptReaderMock);
            //---------------Assert Precondition----------------
            Assert.AreEqual(1, scriptReaderMock.SqlStatements.Count);
            //---------------Execute Test ----------------------
            sqlScriptRunner.ExecuteScriptAgainstMasterDB("Script name not needed in this test");
            //---------------Test Result -----------------------
            Assert.IsTrue(scriptReaderMock.LoadScriptWasCalled);
            utilsDbStub.AssertWasCalled(x => x.ExecuteSql(utilsDbStub.CreateTestDBConnection(), sqlStatement));
           // utilsDbStub.AssertWasCalled(x => x.SwapToMasterDB());
        }

        [Test]
        public void Test_ExecuteScriptAgainstMasterDB_WhenCalledWithSqlScriptReaderHavingOneSqlStatement_ShouldReturnTheOrigionalTestDatabaseName()
        {
            //---------------Set up test pack-------------------

            var sqlStatement = GetRandomString();
            var utilsDbStub = MockRepository.GenerateStub<TestUtilsDB_SqlServer>();
            string originalTestDBName = GetRandomString();
            utilsDbStub.TestDatabaseName = originalTestDBName;
            var scriptReaderMock = new SqlScriptReaderMock();
            scriptReaderMock.AddTestSqlStatment(sqlStatement);
            var sqlScriptRunner = new SqlScriptRunner(utilsDbStub, scriptReaderMock);
            //---------------Assert Precondition----------------
            Assert.AreEqual(1, scriptReaderMock.SqlStatements.Count);
            //---------------Execute Test ----------------------
            sqlScriptRunner.ExecuteScriptAgainstMasterDB("Script name not needed in this test");
            //---------------Test Result -----------------------
            Assert.AreEqual(originalTestDBName, utilsDbStub.TestDatabaseName, "Should set DBNameBack to orig");
        }
        // ReSharper restore InconsistentNaming

        private static string GetRandomString()
        {
            return TestUtilsShared.GetRandomString();
        }
    }

    internal class SqlScriptReaderMock : SqlScriptReader
    {

        public bool LoadScriptWasCalled { get; private set; }

        public void AddTestSqlStatment(string sqlStatement)
        {
            base.SqlStatements.Add(sqlStatement);
        }


        public override void LoadScript(string sqlSctiptFileName)
        {
            LoadScriptWasCalled = true;
        }
    }
}